#include "utils/utils/string_util.h"
#include <string.h>
#include <algorithm>
#include <regex>

namespace base
{
namespace utils
{
StringUtil::StringUtil() = default;

std::string StringUtil::toLower(const std::string &origin_str)
{
    std::string result = origin_str;
    std::transform(result.begin(), result.end(), result.begin(), ::tolower);
    return result;
}
std::string StringUtil::toUpper(const std::string &origin_str)
{
    std::string result = origin_str;
    std::transform(result.begin(), result.end(), result.begin(), ::toupper);
    return result;
}
bool StringUtil::startsWith(const std::string &origin_str, const std::string &substr, CaseSensitivity cs)
{
    if (CaseSensitivity::CaseSensitive == cs)
    {
        return origin_str.find(substr) == 0;
    }
    return toLower(origin_str).find(toLower(substr)) == 0;
}
bool StringUtil::endsWith(const std::string &origin_str, const std::string &substr, CaseSensitivity cs)
{
    std::size_t last_index = origin_str.length() - substr.length();
    if (CaseSensitivity::CaseSensitive == cs)
    {
        return origin_str.rfind(substr) == last_index;
    }
    return toLower(origin_str).rfind(toLower(substr)) == last_index;
}
int StringUtil::compare(const std::string &s1, const std::string &s2, CaseSensitivity cs)
{
    if (CaseSensitivity::CaseSensitive == cs)
    {
        return strcmp(s1.c_str(), s2.c_str());
    }
    return strcmp(toLower(s1).c_str(), toLower(s2).c_str());
}
bool StringUtil::contains(const std::string &str, const std::string &substr, CaseSensitivity cs)
{
    if (CaseSensitivity::CaseSensitive == cs)
    {
        return str.find(substr) != std::string::npos;
    }
    return toLower(substr).find(toLower(substr)) != std::string::npos;
}
std::vector<std::string> StringUtil::split(const std::string &str, const std::string &sep, SplitBehavior behavior, CaseSensitivity cs)
{
    std::string check_str = CaseSensitivity::CaseSensitive == cs ? str : toLower(str);
    std::string check_sep = CaseSensitivity::CaseSensitive == cs ? sep : toLower(sep);

    size_t length = check_sep.length();
    std::size_t index = check_str.find(check_sep);
    std::size_t start_index = 0;
    std::string element;

    std::vector<std::string> result;
    while (true)
    {
        element = str.substr(start_index, index - start_index);
        if (!element.empty() || (element.empty() && SplitBehavior::KeepEmptyParts == behavior))
        {
            result.emplace_back(element);
        }
        if (std::string::npos == index)
        {
            break;
        }
        start_index = index + length;
        index = check_str.find(check_sep, start_index);
    }
    return result;
}
std::string StringUtil::trimLeft(const std::string &str, const std::string &token)
{
    std::string result = str;
    result.erase(0, result.find_first_not_of(token));
    return result;
}
std::string StringUtil::trimRight(const std::string &str, const std::string &token)
{
    std::string result = str;
    result.erase(result.find_last_not_of(token) + 1);
    return result;
}
std::string StringUtil::trim(const std::string &str, const std::string &token)
{
    std::string result = str;
    result.erase(0, result.find_first_not_of(token));
    result.erase(result.find_last_not_of(token) + 1);
    return result;
}
std::string StringUtil::trimmed(const std::string &str)
{
    std::string result = str;
    std::string token = "\t\n\v\f\r ";
    result.erase(0, result.find_first_not_of(token));
    result.erase(result.find_last_not_of(token) + 1);
    return result;
}
std::string StringUtil::replace(const std::string &str, const std::string &src, const std::string &dest)
{
    std::string result;
    std::string::size_type pos_begin = 0;
    std::string::size_type pos = str.find(src);
    while (std::string::npos != pos)
    {
        result.append(str, pos_begin, pos - pos_begin);
        result.append(dest);
        pos_begin = pos + src.length();
        pos = str.find(src, pos_begin);
    }
    if (pos_begin < str.length())
    {
        result.append(str.begin() + pos_begin, str.end());
    }

    return result;
}
bool StringUtil::isIdentifier(const std::string &str)
{
    return std::regex_match(str, std::regex("\\w+"));
}
bool StringUtil::isDigit(const std::string &str)
{
    return std::regex_match(str, std::regex("\\d+"));
}

}
}
